using System;
using System.Collections;

using Evaluant.OPath;

namespace Evaluant.Uss.ObjectContext.Navigator
{
	/// <summary>
	/// Compares two objects for equivalence, based on an opath expression
	/// </summary>
	public class NavigationComparer : IComparer
	{
		OPathQuery[] _Query;
		Navigator _Navigator;

		string[] sortAttributes;
		int[] sortDirection;

		private const string ASC = "asc";
		private const string DESC = "desc";

		/// <summary>
		/// Creates a new <see cref="NavigationComparer"/> instance.
		/// </summary>
		/// <param name="path">Path.</param>
		public NavigationComparer(string path)
		{
			if(path == null)
				throw new ArgumentNullException("path");

			sortAttributes = path.Split(',');
			sortDirection = new int[sortAttributes.Length];
			_Query = new OPathQuery[sortAttributes.Length];

			for(int i=0; i<sortAttributes.Length; i++)
			{
				string[] values = sortAttributes[i].Trim().Split();

				if(values.Length > 2)
					throw new ArgumentException("path");

				sortAttributes[i] = values[0].Trim();
				string dir = (values.Length == 2) ? values[1].Trim().ToLower() : ASC;
				sortDirection[i] = dir == ASC ? 1 : -1;

				_Query[i] = new OPathQuery(String.Format("this[{0}]", sortAttributes[i]));
				_Query[i].Compile();
			}

			_Navigator = new Navigator();
		}


		/// <summary>
		/// Performs a comparison of two objects of the same type and returns a value indicating whether one is less than, equal to or greater than the other.
		/// </summary>
		/// <param name="x">The first object to compare.</param>
		/// <param name="y">The second object to compare.</param>
		/// <returns></returns>
		public int Compare(object x, object y)
		{
			if(x == y)
				return 0;

			int result = 0;
			int numAtt = 0;

			while(numAtt < _Query.Length)
			{
				_Navigator.Roots.Clear();
				_Navigator.Roots.Add(x);
				object xValue = _Navigator.Evaluate(_Query[numAtt].Path);

				_Navigator.Roots.Clear();
				_Navigator.Roots.Add(y);
				object yValue = _Navigator.Evaluate(_Query[numAtt].Path);

				if(xValue == null && yValue == null)
					result = 0;

				if(xValue == null)
					return -1 * sortDirection[numAtt];

				if(yValue == null)
					return sortDirection[numAtt];

				result = sortDirection[numAtt] * Comparer.Default.Compare(xValue, yValue);
				if(result != 0)
					return result;

				numAtt++;
			}

			return 0;

		}
	}
}
